package com.directions.android.screens;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import com.directions.android.BundleUtils;
import com.directions.android.DialogUtils;
import com.directions.android.FilterWithSpaceAdapter;
import com.directions.android.R;
import com.directions.android.customgeocoder.Address;
import com.directions.android.customgeocoder.Geocoder;

public class MenuFragment extends Fragment {

	private static final String MENU_FRAGMENT_TAG = "MenuFragment";

	private AutoCompleteTextView processingLocation;
	private AutoCompleteTextView destination;
	private AutoCompleteTextView origin;
	private OnShowMapListener mCallback;
	private Address origAddr, dstAddr;
	private RadioGroup rgRoute;
	private View rootView;

	public static Fragment getInstance() {
		Fragment menuFragment = new MenuFragment();
		return menuFragment;
	}

	// Container Activity must implement this interface
	public interface OnShowMapListener {
		public void onShowMap(Bundle bundle);
	}

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);

		// This makes sure that the container activity has implemented
		// the callback interface. If not, it throws an exception
		try {
			mCallback = (OnShowMapListener) activity;
		} catch (ClassCastException e) {
			throw new ClassCastException(activity.toString()
					+ " must implement OnHeadlineSelectedListener");
		}
	}

	public MenuFragment() {
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		super.onCreateView(inflater, container, savedInstanceState);
		rootView = inflater.inflate(R.layout.menu, container, false);

		origin = (AutoCompleteTextView) rootView
				.findViewById(R.id.origin_input);

		destination = (AutoCompleteTextView) rootView
				.findViewById(R.id.destination_input);

		// Set up the location history shown by the origin and destination
		String locationHistory = PreferenceManager.getDefaultSharedPreferences(
				getActivity()).getString(
				PreferenceFragment.DELETE_LOCATION_HISTORY,
				"Mi posición actual"/* getString(R.string.my_location) */);

		String[] locHistory = locationHistory
				.split(PreferenceFragment.DELETE_LOCATION_HISTORY_SEPARATOR);

		FilterWithSpaceAdapter<String> historyAdapter;
		historyAdapter = new FilterWithSpaceAdapter<String>(getActivity(),
				android.R.layout.simple_dropdown_item_1line, locHistory);

		origin.setAdapter(historyAdapter);
		destination.setAdapter(historyAdapter);

		// Init RadioGroup Widget GUI
		rgRoute = (RadioGroup) rootView.findViewById(R.id.rgRoute);

		final Button getDir = (Button) rootView.findViewById(R.id.button1);
		getDir.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				String locationName = null;

				// Hide the keyboard
				InputMethodManager imm = (InputMethodManager) getActivity()
						.getSystemService(Context.INPUT_METHOD_SERVICE);

				String mCurrPos = "Mi posición actual";
				/* getString(R.string.my_location) */

				// Set location name member, switch to UI thread and geocode
				if (processingLocation == null) {
					processingLocation = origin;
					imm.hideSoftInputFromWindow(origin.getWindowToken(), 0);
					// Get the location text
					locationName = origin.getText().toString();

					// If the user chooses my current position option
					// we must treat it as a special case
					if (locationName.equalsIgnoreCase(mCurrPos)) {

						locationName = destination.getText().toString();
						if (!locationName.equalsIgnoreCase(mCurrPos)) {
							processingLocation = destination;
						} else {
							displayMap();
							return;
						}
					}

				} else {

					locationName = destination.getText().toString();
					if (locationName.equalsIgnoreCase(mCurrPos)) {
						displayMap();
						return;
					}

					// Get the location text
					processingLocation = destination;
					imm.hideSoftInputFromWindow(destination.getWindowToken(), 0);
				}

				GeocodeTask geocodeTask = new GeocodeTask();
				geocodeTask.execute(locationName);
			}
		});

		return rootView;
	}

	/**
	 * Invoked if the origin input or destination input equals ignore case
	 * "My current position" option. Decides whether to pass latitude or
	 * longitude values to the {@link MapFragment} or just booleans indicating
	 * that the user wants to display the route from it's current position.
	 * 
	 * @param origIsCurrPos
	 * @param dstIsCurrentPos
	 */
	private void displayMap() {
		Bundle mapInfo = new Bundle();

		if (origAddr == null) {
			mapInfo.putBoolean(BundleUtils.CURR_POS_ORIG, true);
		} else {
			mapInfo.putDouble(BundleUtils.ORIG_LAT, origAddr.getLatitude());
			mapInfo.putDouble(BundleUtils.ORIG_LONG, origAddr.getLongitude());
		}

		if (dstAddr == null) {
			mapInfo.putBoolean(BundleUtils.CURR_POS_DST, true);
		} else {
			mapInfo.putDouble(BundleUtils.DST_LAT, dstAddr.getLatitude());
			mapInfo.putDouble(BundleUtils.DST_LONG, dstAddr.getLongitude());
		}

		// Get Selected Radio Button and display output
		RadioButton selectRadio = (RadioButton) rootView.findViewById(rgRoute
				.getCheckedRadioButtonId());
		mapInfo.putString(BundleUtils.ROUTE, selectRadio.getText().toString());

		dstAddr = null;
		origAddr = null;
		processingLocation = null;
		mCallback.onShowMap(mapInfo);
	}

	// ////////////////////
	// GEOCOE LOCATION
	// ////////////////////

	public List<Address> geocodeLocation(String locationName) {
		// Geocode the location
		Geocoder geocoder = new Geocoder();
		SharedPreferences sp = PreferenceManager
				.getDefaultSharedPreferences(getActivity());
		boolean useRegionBias = sp.getBoolean(PreferenceFragment.REGION_BIAS,
				true);
		geocoder.setUseRegionBias(useRegionBias);
		try {
			List<Address> addresses = geocoder
					.getFromLocationName(locationName);
			return addresses;
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	public class GeocodeTask extends AsyncTask<String, Void, List<Address>> {
		// Declare
		private ProgressDialog mPleaseWaitDialog = null;

		public void showLocationPendingDialog() {
			if (mPleaseWaitDialog != null) {
				return;
			}

			mPleaseWaitDialog = new ProgressDialog(getActivity());
			mPleaseWaitDialog
					.setMessage(getString(R.string.searching_for_location));
			mPleaseWaitDialog.setTitle(R.string.please_wait);
			mPleaseWaitDialog.setIndeterminate(true);
			mPleaseWaitDialog.show();
		}

		public void cancelLocationPendingDialog() {
			if (mPleaseWaitDialog != null) {
				mPleaseWaitDialog.dismiss();
				mPleaseWaitDialog = null;
			}
		}

		@Override
		protected void onPostExecute(List<Address> addresses) {
			super.onPostExecute(addresses);
			cancelLocationPendingDialog();

			checkResults(addresses);
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			showLocationPendingDialog();
		}

		@Override
		protected List<Address> doInBackground(String... args) {
			// Declare
			List<Address> addresses;

			// Extract parameters
			String locationName = args[0];

			// Geocode
			addresses = geocodeLocation(locationName);

			// Return
			return addresses;
		}
	};

	private void displayLocation(List<Address> addresses, Address address) {
		if (processingLocation == null)
			return;

		String currentFormattedAddress = address.getFormattedAddress();
		String userInput = processingLocation.getText().toString();
		addToLocationHistoryIfNeeded(currentFormattedAddress);
		processingLocation.setText(currentFormattedAddress);
		if (processingLocation == destination) {

			dstAddr = address;
			displayMap();

		} else if (processingLocation == origin) {

			origAddr = address;
			Log.d(MENU_FRAGMENT_TAG, "orig: " + origAddr.toString());
			processingLocation = destination;
			String dstInput = destination.getText().toString();

			String mCurrPos = "Mi posición actual";
			/* getString(R.string.my_location) */
			if (dstInput.equalsIgnoreCase(mCurrPos)) {
				displayMap();
				return;
			}

			if (dstInput.equalsIgnoreCase(userInput)) {
				// If the user introduced the same string in origin and
				// destination there is no need to ask Google again for the same
				// thing since we already have the answer
				checkResults(addresses);
			} else {
				GeocodeTask geocodeTask = new GeocodeTask();
				geocodeTask.execute(dstInput);
			}
		}
	}

	private void addToLocationHistoryIfNeeded(String newLocation) {
		SharedPreferences sp = PreferenceManager
				.getDefaultSharedPreferences(getActivity());
		String locationHistory = sp.getString(
				PreferenceFragment.DELETE_LOCATION_HISTORY,
				"Mi posición actual"/* getString(R.string.my_location) */);

		String[] locHistory = locationHistory
				.split(PreferenceFragment.DELETE_LOCATION_HISTORY_SEPARATOR);

		for (int i = 0, length = locHistory.length; i < length; ++i)
			if (locHistory[i].equalsIgnoreCase(newLocation))
				return;
		sp.edit()
				.putString(
						PreferenceFragment.DELETE_LOCATION_HISTORY,
						locationHistory
								+ PreferenceFragment.DELETE_LOCATION_HISTORY_SEPARATOR
								+ newLocation).apply();
	}

	/**
	 * 
	 * @param addresses
	 */
	private void checkResults(List<Address> addresses) {
		// Check the number of results
		if (addresses != null && !addresses.isEmpty()) {
			if (addresses.size() > 1) {
				// Determine which address to display
				showLocationPicker(addresses);
			} else {
				// Display address
				displayLocation(addresses, addresses.get(0));
			}
		} else {
			DialogUtils.showConfirmationAlert(getActivity(),
					getString(R.string.no_results_were_found),
					getString(R.string.error));
		}
	}

	private void showLocationPicker(final List<Address> results) {
		// Check input
		if (results.size() == 0)
			return;

		// Convert the list of results to display strings
		final String[] items = getAddressStringArray(results);

		// Display alert
		DialogUtils.showItemsAlert(getActivity(),
				getString(R.string.did_you_mean), items,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialogInterface,
							int itemIndex) {
						// Display the position
						displayLocation(results, results.get(itemIndex));
					}
				});
	}

	private String[] getAddressStringArray(List<Address> results) {
		// Declare
		ArrayList<String> result = new ArrayList<String>();
		String[] resultType = new String[0];

		// Iterate over addresses
		for (int i = 0; i < results.size(); i++) {
			// Get the data
			String formattedAddress = results.get(i).getFormattedAddress();
			if (formattedAddress == null)
				formattedAddress = "";
			result.add(formattedAddress);
		}

		// Return
		if (result.size() == 0) {
			return null;
		} else {
			return result.toArray(resultType);
		}
	}
}
